/*
 ============================================================================
 Name        : CommandParser.c
 Author      : Sherry Krikszer
 Version     : 1.0
 Copyright   :
 Description : in C, Ansi-style
 ============================================================================
 */

#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include "CommandParser.h"
#include "AssemblerCommon.h"

/* ----------------------------------------------------------------------------
 *	Constants Definition
 * ----------------------------------------------------------------------------*/

#define EQUAL_STRINGS 0

#define IMMEDIATE_START_MARKER '#'

#define INDEXING_MARKER_DELIMITERS "[]"
#define INDEX_MODE_MARKER '['
#define INDEX_MARKER '*'

/* ----------------------------------------------------------------------------
 *	Constants Definition End
 * ----------------------------------------------------------------------------*/

/* ----------------------------------------------------------------------------
 *	Macro Definition
 * ----------------------------------------------------------------------------*/

#define IS_IMMEDIATE(ch) ((ch == IMMEDIATE_START_MARKER) ? TRUE : FALSE)

#define IS_INDEX_MODE(ch) ((ch == INDEX_MODE_MARKER) ? TRUE : FALSE)
#define IS_VALID_REGISTER(reg) (strcmp(reg->registerName, "Invalid") != EQUAL_STRINGS)
#define IS_VALID_INDEX(ch) ((ch == INDEX_MARKER) ? TRUE : FALSE)
#define IS_NUMBER_SIGN(ch) (((ch == POSITIVE_NUMBER_SIGN)|| (ch == NEGATIVE_NUMBER_SIGN)) ? TRUE : FALSE)

/* ----------------------------------------------------------------------------
 *	Macro Definition End
 * ----------------------------------------------------------------------------*/

/* ----------------------------------------------------------------------------
 *	Function Declaration
 * ----------------------------------------------------------------------------*/

Boolean DoesContainCommand(char* commandName);
void ConvertStringToLowerCase(char* string, char* lowerCaseString);

ParseResult ParseMovParams (char** params, int paramCount);
ParseResult ParseCmpParams (char** params, int paramCount);
ParseResult ParseAddParams (char** params, int paramCount);
ParseResult ParseSubParams (char** params, int paramCount);
ParseResult ParseNotParams (char** params, int paramCount);
ParseResult ParseClrParams (char** params, int paramCount);
ParseResult ParseLeaParams (char** params, int paramCount);
ParseResult ParseIncParams (char** params, int paramCount);
ParseResult ParseDecParams (char** params, int paramCount);
ParseResult ParseJmpParams (char** params, int paramCount);
ParseResult ParseBneParams (char** params, int paramCount);
ParseResult ParseRedParams (char** params, int paramCount);
ParseResult ParsePrnParams (char** params, int paramCount);
ParseResult ParseJsrParams (char** params, int paramCount);
ParseResult ParseRstParams (char** params, int paramCount);
ParseResult ParseHltParams (char** params, int paramCount);

/* ----------------------------------------------------------------------------
 *	Function Declaration End
 * ----------------------------------------------------------------------------*/

/* ----------------------------------------------------------------------------
 *	Variable Definition
 * ----------------------------------------------------------------------------*/

Register programRegisters[] =
{
	{"r0", 0},
	{"r1", 1},
	{"r2", 2},
	{"r3", 3},
	{"r4", 4},
	{"r5", 5},
	{"r6", 6},
	{"r7", 7},
	{"Invalid", -1}
};

AddressingMethodDetail AddressingMethodDetailTable[] =
{
	/* IMMEDIATE_ADDRESSING 	= */ {1, 0},
	/* DIRECT_ADDRESSING 		= */ {1, 1},
	/* INDEX_ADDRESSING 	 	= */ {2, 2},
	/* DOUBLE_INDEX_ADDRESSING 	= */ {2, 3},
	/* REGISTER_ADDRESSING 		= */ {0, 4}
};

CommandDetail AvailableCommands[] =
{
	{MOV, 		"mov",		 0, 2, 	ParseMovParams, 	{{-1,  1,  2,  3,  4},  { 0,  1,  2,  3,  4}, 	}, },
	{CMP, 		"cmp",		 1, 2, 	ParseCmpParams,		{{ 0,  1,  2,  3,  4}, { 0,  1,  2,  3,  4}, 	}, },
	{ADD, 		"add",		 2, 2, 	ParseAddParams,		{{-1,  1,  2,  3,  4}, { 0,  1,  2,  3,  4}, 	}, },
	{SUB, 		"sub",		 3, 2, 	ParseSubParams,		{{-1,  1,  2,  3,  4}, { 0,  1,  2,  3,  4}, 	}, },
	{NOT, 		"not",		 4, 1, 	ParseNotParams,		{{-1,  1,  2,  3,  4}, {-1, -1, -1, -1, -1}, 	}, },
	{CLR, 		"clr",		 5, 1, 	ParseClrParams,		{{-1,  1,  2,  3,  4}, {-1, -1, -1, -1, -1}, 	}, },
	{LEA, 		"lea",		 6, 2, 	ParseLeaParams,		{{-1,  1,  2,  3,  4}, {-1,  1,  2,  3, -1}, 	}, },
	{INC, 		"inc",		 7, 1, 	ParseIncParams,		{{-1,  1,  2,  3,  4}, {-1, -1, -1, -1, -1}, 	}, },
	{DEC, 		"dec",		 8, 1, 	ParseDecParams,		{{-1,  1,  2,  3,  4}, {-1, -1, -1, -1, -1}, 	}, },
	{JMP, 		"jmp",		 9, 1, 	ParseJmpParams,		{{-1,  1,  2,  3,  4}, {-1, -1, -1, -1, -1}, 	}, },
	{BNE, 		"bne",		10, 1, 	ParseBneParams,		{{-1,  1,  2,  3,  4}, {-1, -1, -1, -1, -1}, 	}, },
	{RED, 		"red",		11, 1, 	ParseRedParams,		{{-1,  1,  2,  3,  4}, {-1, -1, -1, -1, -1}, 	}, },
	{PRN, 		"prn",		12, 1, 	ParsePrnParams,		{{ 0,  1,  2,  3,  4}, {-1, -1, -1, -1, -1}, 	}, },
	{JSR, 		"jsr",		13, 1, 	ParseJsrParams,		{{-1,  1, -1, -1, -1}, {-1, -1, -1, -1, -1}, 	}, },
	{RTS, 		"rts",		14, 0, 	ParseRstParams,		{{-1, -1, -1, -1, -1}, {-1, -1, -1, -1, -1}, 	}, },
	{HLT, 		"hlt",		15,	0, 	ParseHltParams,		{{-1, -1, -1, -1, -1}, {-1, -1, -1, -1, -1}, 	}, },
	{INVALID,	"invalid",	-1,-1,	NULL,				{{-1, -1, -1, -1, -1}, {-1, -1, -1, -1, -1}, 	}, }
};

/* ----------------------------------------------------------------------------
 *	Variable Definition End
 * ----------------------------------------------------------------------------*/

/* ----------------------------------------------------------------------------
 *	Function Definition
 * ----------------------------------------------------------------------------*/

AddressingMethodDetail GetAddressingMethodDetail(AddressingMethod addressingMethod)
{
	return AddressingMethodDetailTable[addressingMethod];
}


/* ----------------------------------------------------------------------------
 *	Parse Functions
 * ----------------------------------------------------------------------------*/

int WordCountForOperand(AddressingMethod addressingMethodToCheck)
{
	int wordCount = -1;

	if (addressingMethodToCheck != UNDEFINED)
	{
		wordCount = AddressingMethodDetailTable[addressingMethodToCheck].numberOfWordUsed;
	}

	return wordCount;
}

Boolean IsValidAddressingMethod(AddressingMethod addressingMethodToCheck,
		AddressingMethod* allowedAddressingMethods)
{
	Boolean isValidAddressingMethod = FALSE;
	int index;

	for (index = 0; index < MAX_ADDRESSING_METHODS_NUMBER; index++)
	{
		if (addressingMethodToCheck == allowedAddressingMethods[index])
		{
			isValidAddressingMethod = TRUE;
			break;
		}
	}

	return isValidAddressingMethod;
}

/*Boolean IsValidRegisterName(char* nameString)
{

}*/

Register* GetRegister(char* registerName)
{
	int RegCounter = 0;
	Register* reg = &(programRegisters[RegCounter]);

	while(IS_VALID_REGISTER(reg))
	{
		if (strcmp(reg->registerName, registerName) == EQUAL_STRINGS)
		{
			return reg;
		}

		RegCounter++;

		reg = &(programRegisters[RegCounter]);
	}

	return reg;
}


Boolean ParseParam(char* paramString, Operand* operand, AddressingMethod* allowedAddressingMethods,
	char* errorMessage)
{
	/* Attempt to get register or first symbol*/
	Register* reg = GetRegister(paramString);
	char* label = strtok(paramString, INDEXING_MARKER_DELIMITERS);

	if (IS_IMMEDIATE(*paramString) == TRUE)
	{
		if (IsValidAddressingMethod(IMMEDIATE_ADDRESSING, allowedAddressingMethods) == TRUE)
		{
			/* Check is valid number , skip the pound sign*/
			if (IsValidNumberParameter(++paramString) == TRUE)
			{
				operand->operandAdressingMethod = IMMEDIATE_ADDRESSING;
				operand->operandBaseWord = atoi(paramString);
				operand->linkingOptionBaseWord = ABSOLUTE;
				return TRUE;
			}
		}
		else
		{
			errorMessage = "Invalid addressing mode";
			return FALSE;
		}
	}
	else if (IS_VALID_REGISTER(reg))
	{
		if (IsValidAddressingMethod(REGISTER_ADDRESSING, allowedAddressingMethods) == TRUE)
		{
			/* Check is valid number */
			operand->operandAdressingMethod = REGISTER_ADDRESSING;
			operand->operandBaseWord = reg->registerAddress;
			return TRUE;
		}
		else
		{
			errorMessage = "Invalid addressing mode";
			return FALSE;
		}
	}
	else if (label)
	{
		//could be relative or double idnexing
		char* labels[3] = {label, NULL, NULL};
		labels[1] = strtok(NULL, "[]");
		/*check the next label*/
		if (IsValidLabel(label, errorMessage) == TRUE)
		{
			if (labels[1] != NULL)
			{
				if (IS_VALID_INDEX(*(labels[1])))
				{
					// index addressing
					if (IsValidAddressingMethod(INDEX_ADDRESSING, allowedAddressingMethods) == TRUE)
					{
						/* Check is valid number */
						operand->operandAdressingMethod = INDEX_ADDRESSING;
						strcpy(operand->operandBaseSymbol, labels[0]);
						/* skip the star sign */
						strcpy(operand->operandExtendSymbol, labels[1] + 1);
						return TRUE;
					}
					else
					{
						errorMessage = "Invalid addressing mode";
						return FALSE;

					}
				}
				else
				{
					//could be double idexing check third label is register
					labels[2] = strtok(NULL, "[]");
					if (label[2])
					{
						reg = GetRegister(labels[2]);
						if (IS_VALID_REGISTER(reg))
						{
							//double indexing
							if (IsValidAddressingMethod(DOUBLE_INDEX_ADDRESSING, allowedAddressingMethods) == TRUE)
							{
								/* Check is valid number */
								operand->operandAdressingMethod = DOUBLE_INDEX_ADDRESSING;
								strcpy(operand->operandBaseSymbol, labels[0]);
								strcpy(operand->operandExtendSymbol, labels[1]);
								operand->registerOperand.registerAddress = reg->registerAddress;
								return TRUE;
							}
							else
							{
								errorMessage = "Invalid addressing mode";
								return FALSE;
							}
						}
						else
						{
							// not a register
							errorMessage = "Invalid register for double addressing";
							return FALSE;
						}
					}
					else
					{
						errorMessage = "Invalid syntax for index";
						return FALSE;
					}
				}
			}
			else
			{
				/* Direct addressing */
				operand->operandAdressingMethod = DIRECT_ADDRESSING;
				strcpy(operand->operandBaseSymbol, label);
				return TRUE;
			}
		}
		else
		{
			errorMessage = "Bad label syntex";
			return FALSE;
		}

	}
	else
	{
		errorMessage = "Unknown operand type";
		return FALSE;
	}

	return FALSE; /* TODO : CHANGE THIS !!!!!!!!*/
}

void InitCommandOperands (CommandOperands* commandOperands)
{
	commandOperands->sizeInWords = 0;
	commandOperands->operandCount = 0;
}

ParseResult ParseAssemblerCommand (char* commandName, Params params)
{
	ParseResult commandParseResult;
	Boolean parseErrorFound = FALSE;
	/* TODO : check params */
	int index;

	CommandOperands commandOperands;
	CommandDetail* commandDetail;
	InitCommandOperands(&commandOperands);

	commandDetail = GetCommandDetailByName(commandName);

	if (commandDetail->commandType != INVALID)
	{
		commandOperands.commandType = commandDetail->commandType;

		//reserve one word for comannd encoding
		commandOperands.sizeInWords++; 
		if (commandDetail->expectedParametersCount == params.paramCounter)
		{
			int index = 0;

			

			while ((index < params.paramCounter) && (parseErrorFound == FALSE))
			{
				/* Get current command parameter */
				char* currParam = params.paramValues[index];

				Operand operandToSet;

				if (ParseParam(currParam, &operandToSet,
						commandDetail->allowedAddressingMethods[index],
						commandParseResult.parsingErrorMessage) == TRUE)
				{
					commandOperands.operands[index] = operandToSet;
					commandOperands.sizeInWords += WordCountForOperand(
							operandToSet.operandAdressingMethod);
					commandOperands.operandCount++;

					
				}
				else
				{
					parseErrorFound = TRUE;
				}

				index++;
			}

			commandParseResult.parsedCommand = commandOperands;
		}
		else
		{
			/* Notify invalid parameters number */
			sprintf(commandParseResult.parsingErrorMessage,
					"Invalid use of command, \"%s\" expects %d parameters, "
					"yet %d parameters where supplied ",
					commandName, commandDetail->expectedParametersCount);
		}
	}
	else
	{
		/* Notify invalid command name */
		sprintf(commandParseResult.parsingErrorMessage,
				"Invalid command name, \"%s\" is an unknown command", commandName);
	}
	commandParseResult.hasParseSucceded = (parseErrorFound == FALSE) ? TRUE : FALSE;
	return commandParseResult;
}

ParseResult ParseMovParams (char** params, int paramCount)
{
	ParseResult movParseResult;

//	/* TODO : check params */
//	int index;
//
//	CommandOperands commandOperands;
//	InitCommandOperands(&commandOperands);
//
//	for (index = 0; index < params.paramCounter; index++)
//	{
//		/* Get current command parameter */
//		char* currParam = params.paramValues[index];
//
//		/* TODO : change access to AvailableParsers */
//		Operand operandToSet;
//
//		if (ParseParam(currParam, &operandToSet,
//				AvailableCommands[MOV].allowedAddressingMethods[index]) == TRUE)
//		{
//			commandOperands.operands[index] = operandToSet;
//			commandOperands.sizeInWords += WordCountForOperand;
//			commandOperands.operandCount++;
//		}
//	}

	return movParseResult;
}

ParseResult ParseCmpParams (char** params, int paramCount)
{
	ParseResult cmpParseResult;



	return cmpParseResult;
}

ParseResult ParseAddParams (char** params, int paramCount)
{
	ParseResult addParseResult;



	return addParseResult;
}

ParseResult ParseSubParams (char** params, int paramCount)
{
	ParseResult subParseResult;



	return subParseResult;
}

ParseResult ParseNotParams (char** params, int paramCount)
{
	ParseResult notParseResult;



	return notParseResult;
}

ParseResult ParseClrParams (char** params, int paramCount)
{
	ParseResult clrParseResult;



	return clrParseResult;
}

ParseResult ParseLeaParams (char** params, int paramCount)
{
	ParseResult leaParseResult;



	return leaParseResult;
}

ParseResult ParseIncParams (char** params, int paramCount)
{
	ParseResult incParseResult;



	return incParseResult;
}

ParseResult ParseDecParams (char** params, int paramCount)
{
	ParseResult decParseResult;



	return decParseResult;
}

ParseResult ParseJmpParams (char** params, int paramCount)
{
	ParseResult jmpParseResult;



	return jmpParseResult;
}

ParseResult ParseBneParams (char** params, int paramCount)
{
	ParseResult bneParseResult;



	return bneParseResult;
}

ParseResult ParseRedParams (char** params, int paramCount)
{
	ParseResult redParseResult;



	return redParseResult;
}

ParseResult ParsePrnParams (char** params, int paramCount)
{
	ParseResult prnParseResult;



	return prnParseResult;
}

ParseResult ParseJsrParams (char** params, int paramCount)
{
	ParseResult jsrParseResult;



	return jsrParseResult;
}

ParseResult ParseRstParams (char** params, int paramCount)
{
	ParseResult rstParseResult;



	return rstParseResult;
}

ParseResult ParseHltParams (char** params, int paramCount)
{
	ParseResult hltParseResult;



	return hltParseResult;
}

/* ----------------------------------------------------------------------------
 *	Parse Functions End
 * ----------------------------------------------------------------------------*/

CommandDetail* GetCommandDetailByType(CommandType commandTypeToSearch)
{
	int commandIndex = 0;

	CommandDetail* currCommandDetail = &(AvailableCommands[commandIndex]);

	commandIndex++;

	/* Find the command requested by iterating through the available commands */
	while (currCommandDetail->commandType != INVALID)
	{
		if (commandTypeToSearch == currCommandDetail->commandType)
		{
			break;
		}

		currCommandDetail = &(AvailableCommands[commandIndex]);

		commandIndex++;
	}

	return currCommandDetail;
}

CommandDetail* GetCommandDetailByName(char* commandName)
{
	int commandIndex = 0;

	char commandNameToSearch[MAX_COMMAND_NAME_LENGTH];
	CommandDetail* currCommandDetail;

	ConvertStringToLowerCase(commandName, commandNameToSearch);

	currCommandDetail = &(AvailableCommands[commandIndex]);

	commandIndex++;

	/* Find the command requested by iterating through the available commands */
	while (currCommandDetail->commandType != INVALID)
	{
		if (strcmp(commandNameToSearch, currCommandDetail->commandName) == EQUAL_STRINGS)
		{
			break;
		}

		currCommandDetail = &(AvailableCommands[commandIndex]);
		commandIndex++;
	}

	return currCommandDetail;
}

/* Returns the parser that matches the given command name */
CommandParser GetMatchingCommandParser(char* commandName)
{
	return GetCommandDetailByName(commandName)->commandParser;
}

void ConvertStringToLowerCase(char* string, char* lowerCaseString)
{
	int length = strlen(string);
	int index;

	for (index = 0; index < length; index++)
	{
		lowerCaseString[index] = tolower((string[index]));
	}
	lowerCaseString[length] = '\0';
}

Boolean DoesContainCommand(char* commandName)
{
	Boolean containsCommand = FALSE;

	if (GetCommandDetailByName(commandName)->commandType != INVALID)
	{
		containsCommand = TRUE;
	}

	return containsCommand;
}

Boolean IsValidLabel (char* symbol, char* invalidMessage)
{
	Boolean isValidLabel = FALSE;

	int symbolLength = strlen(symbol);

	if (symbolLength > 0)
	{
		/* Check that the label*/
		if (isalpha(*symbol))
		{
			Boolean areAllNumbersOrLetters = TRUE;

			int index;

			/* First character was checked, start loop at the second character
			 * And verify that all following characters are numeric or alphabetic */
			for (index = 1; index < symbolLength; index++)
			{
				if (!isalnum(symbol[index]))
				{
					areAllNumbersOrLetters = FALSE;
				}
			}

			if (areAllNumbersOrLetters == TRUE)
			{
				isValidLabel = TRUE;
			}
			else
			{
				invalidMessage = "Invalid symbol name, symbol name consists of letters and digits only";
			}
		}
		else
		{
			invalidMessage = "Invalid symbol name, symbol name must start with a letter";
		}
	}

	return isValidLabel;
}

Boolean IsValidNumberParameter(char* numberParameterString)
{
	Boolean isValidNumberParameter = FALSE;

	char* currentCharacter = numberParameterString;

	if (IS_NUMBER_SIGN(*currentCharacter) || isdigit(*currentCharacter))
	{
		Boolean areAllDigits = TRUE;

		/* Advance to the next character */
		currentCharacter++;

		while (*currentCharacter != LINE_END && areAllDigits)
		{
			if (!isdigit(*currentCharacter))
			{
				areAllDigits = FALSE;
			}

			/* Advance to the next character */
			currentCharacter++;
		}

		if (areAllDigits == TRUE)
		{
			isValidNumberParameter = TRUE;
		}
	}

	return isValidNumberParameter;
}

Boolean IsValidStringParameter (char* parameterString, char* invalidMessage)
{
	Boolean isValidStringParameter = FALSE;

	char* currentCharacter = parameterString;

	Boolean areAllVisibleCharacters = TRUE;
	Boolean areAllCharactersFoundInsideStringMarkers = TRUE;

	if (*currentCharacter == STRING_START_MARKER)
	{
		Boolean stringEndEncountered = FALSE;
		/* Advance to the next character */
		currentCharacter++;

		while ((areAllVisibleCharacters == TRUE) && (areAllCharactersFoundInsideStringMarkers == TRUE) &&
				(*currentCharacter != LINE_END))
		{
			if (stringEndEncountered == FALSE)
			{
				if (*currentCharacter == STRING_END_MARKER)
				{
					stringEndEncountered = TRUE;
				}
				else if (!isprint(*currentCharacter))
				{
					areAllVisibleCharacters = FALSE;
				}
			}
			else
			{
				areAllCharactersFoundInsideStringMarkers = FALSE;
			}

			/* Advance to the next character */
			currentCharacter++;
		}

		if (areAllVisibleCharacters == TRUE)
		{
			if (areAllCharactersFoundInsideStringMarkers == TRUE)
			{
				isValidStringParameter = TRUE;
			}
			else
			{
				sprintf(invalidMessage, "character '%c' found after string end marker",
						*currentCharacter);
			}
		}
		else
		{
			sprintf(invalidMessage, "Invalid string, only printable characters are allowed");
		}
	}
	else
	{
		sprintf(invalidMessage, "invalid .string start, '%c' was found instead of string start marker",
				*currentCharacter);
	}

	return isValidStringParameter;
}

/* ----------------------------------------------------------------------------
 *	Function Definition End
 * ----------------------------------------------------------------------------*/

